/*--------------------------------------------------------------------------
 * ginxlog.h
 *	  postgres 倒排索引 xlog 实现的头文件
 *
 *	Copyright (c) 2006-2022, PostgreSQL Global Development Group
 *
 *	src/include/access/ginxlog.h
 *--------------------------------------------------------------------------
 */
#ifndef GINXLOG_H
#define GINXLOG_H

#include "access/ginblock.h"
#include "access/itup.h"
#include "access/xlogreader.h"
#include "lib/stringinfo.h"
#include "storage/off.h"

#define XLOG_GIN_CREATE_PTREE  0x10

typedef struct ginxlogCreatePostingTree
{
	uint32		size;
	/* 一个压缩的发布列表紧随其后 */
} ginxlogCreatePostingTree;

/*
 * 插入记录的格式因页面类型而异。
 * ginxlogInsert 是所有变体之间的共同部分。
 *
 * 备份块 0：目标页面
 * 备份块 1：左子节点，如果此插入完成了不完整的拆分
 */

#define XLOG_GIN_INSERT  0x20

typedef struct
{
	uint16		flags;			/* GIN_INSERT_ISLEAF 和/或 GIN_INSERT_ISDATA */

	/*
	 * 后续内容：
	 *
	 * 1. 如果不是叶页面，左子页面和右子页面的块编号
	 * 这是此插入完成的分裂，作为BlockIdData[2]（注意
	 * 在这个结构中添加字段会使它们不16位对齐）
	 *
	 * 2. 一个ginxlogInsertEntry或ginxlogRecompressDataLeaf结构，具体取决于树类型。
	 *
	 * 注意：下面的结构仅在附加到
	 * ginxlogInsert结构时才是16位对齐的！注意添加字段，会要求更严格的对齐。
	 */
} ginxlogInsert;

typedef struct
{
	OffsetNumber offset;
	bool		isDelete;
	IndexTupleData tuple;		/* 可变长度 */
} ginxlogInsertEntry;


typedef struct
{
	uint16		nactions;

	/* 后续的“动作”数量不固定 */
} ginxlogRecompressDataLeaf;

/*
 * 注：此结构当前未在代码中使用，仅作为
 * 文档。WAL记录格式如下所述，但代码
 * 通过指针和memcpy直接访问以读/写这些。
 */
typedef struct
{
	uint8		segno;			/* 此操作适用的段 */
	char		type;			/* 操作类型（见下文） */

	/*
	 * 特定于操作的数据后续。对于INSERT和REPLACE操作，这是一个
	 * GinPostingList结构。对于ADDITEMS，一个uint16表示添加的项目数量，
	 * 后面跟着作为ItemPointers的项目本身。DELETE操作
	 * 没有进一步的数据。
	 */
}			ginxlogSegmentAction;

/* 操作类型 */
#define GIN_SEGMENT_UNMODIFIED	0	/* 无操作（未在WAL记录中使用） */
#define GIN_SEGMENT_DELETE		1	/* 一个完整段被移除 */
#define GIN_SEGMENT_INSERT		2	/* 一个完整段被添加 */
#define GIN_SEGMENT_REPLACE		3	/* 一个段被替换 */
#define GIN_SEGMENT_ADDITEMS	4	/* 项目被添加到现有段 */

typedef struct
{
	OffsetNumber offset;
	PostingItem newitem;
} ginxlogInsertDataInternal;

/*
 * 备份块0：新的左页面（= 原始页面，如果不是根分裂）
 * 备份块1：新的右页面
 * 备份块2：原始页面/新的根页面，如果根分裂
 * 备份块3：左子页面，如果此插入完成了早期的分裂
 */
#define XLOG_GIN_SPLIT	0x30

typedef struct ginxlogSplit
{
	RelFileNode node;
	BlockNumber rrlink;			/* 右链接，或根的块编号如果根
								 * 分裂 */
	BlockNumber leftChildBlkno; /* 在非叶分裂时有效 */
	BlockNumber rightChildBlkno;
	uint16		flags;			/* 见下文 */
} ginxlogSplit;

/*
 * 在ginxlogInsert和ginxlogSplit记录中使用的标志
 */
#define GIN_INSERT_ISDATA	0x01	/* 对于插入和分裂记录均适用 */
#define GIN_INSERT_ISLEAF	0x02	/* 同上 */
#define GIN_SPLIT_ROOT		0x04	/* 仅用于分裂记录 */

/*
 * Vacuum 会在任何修改时简单地 WAL 记录整个页面。这在功能上与 XLOG_FPI 记录相同，但为了调试目的而被分开。 (在检查 WAL 流时，标记为 GIN vacuum 记录而不是堆记录，更容易了解发生了什么。) 目前这仅用于条目树叶页面。
 */
#define XLOG_GIN_VACUUM_PAGE	0x40

/*
 * 清理发布树叶页面时，WAL记录方式与因插入而引起的重压缩相同。
 */
#define XLOG_GIN_VACUUM_DATA_LEAF_PAGE	0x90

typedef struct ginxlogVacuumDataLeafPage
{
	ginxlogRecompressDataLeaf data;
} ginxlogVacuumDataLeafPage;

/*
 * 备份块 0：已删除页面
 * 备份块 1：父级
 * 备份块 2：左侧兄弟
 */
#define XLOG_GIN_DELETE_PAGE	0x50

typedef struct ginxlogDeletePage
{
	OffsetNumber parentOffset;
	BlockNumber rightLink;
	TransactionId deleteXid;	/* 最后一个能够在扫描中看到此页面的 Xid */
} ginxlogDeletePage;

#define XLOG_GIN_UPDATE_META_PAGE 0x60

/*
 * 备份块 0：元页面
 * 备份块 1：尾页面
 */
typedef struct ginxlogUpdateMeta
{
	RelFileNode node;
	GinMetaPageData metadata;
	BlockNumber prevTail;
	BlockNumber newRightlink;
	int32		ntuples;		/* 如果 ntuples > 0 则 metadata.tail 被更新为
								 * 该数量的元组；否则插入了新的子
								 * 列表 */
	/* 插入元组的数组在后面 */
} ginxlogUpdateMeta;

#define XLOG_GIN_INSERT_LISTPAGE  0x70

typedef struct ginxlogInsertListPage
{
	BlockNumber rightlink;
	int32		ntuples;
	/* 插入元组的数组在后面 */
} ginxlogInsertListPage;

/*
 * 备份块 0：元页面
 * 备份块 1 到 (ndeleted + 1)：已删除页面
 */

#define XLOG_GIN_DELETE_LISTPAGE  0x80

/*
 * 删除列表页面的 WAL 记录必须包含对所有已删除页面的块引用，因此一次记录可以删除的页面数量受 XLR_MAX_BLOCK_ID 的限制。 (块 ID 0 用于元页面。)
 */
#define GIN_NDELETE_AT_ONCE Min(16, XLR_MAX_BLOCK_ID - 1)
typedef struct ginxlogDeleteListPages
{
	GinMetaPageData metadata;
	int32		ndeleted;
} ginxlogDeleteListPages;

extern void gin_redo(XLogReaderState *record);
extern void gin_desc(StringInfo buf, XLogReaderState *record);
extern const char *gin_identify(uint8 info);
extern void gin_xlog_startup(void);
extern void gin_xlog_cleanup(void);
extern void gin_mask(char *pagedata, BlockNumber blkno);

#endif							/* GINXLOG_H */
